package three_star_craft;

import java.awt.GraphicsEnvironment;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;

import javax.sound.sampled.AudioSystem;

import org.grlea.log.DebugLevel;
import org.grlea.log.SimpleLogger;

import three_star_craft.gui.GuiMain;
import three_star_craft.uap.UapConnectionListener;
import three_star_craft.uap.UapGenerator;
import three_star_craft.uap.UapSgwProxy;
import three_star_craft.uap.state.UapStateMap;
import ProtocolsCommon.Common.PlayableItem;
import UserApplicationPart.UAP;


/**
 * Main class for user client program
 * Depending on the environment it launches GUI or console menu.
 * 
 * @author w.antosiewic
 *
 */
public class ClientMain implements UapConnectionListener
{
    private static final SimpleLogger logger = new SimpleLogger(ClientMain.class);
	
    private static final String configFileName = "config.properties";

    
    private GuiMain _gui;
    
    private int _clientId;
    
    private UapGenerator _uapGenerator;
    private List<PlayableItem> _availableMedia;
    
    /**
     * Creates main class of user_client.
     * Initializes object by reading property file
     */
    public ClientMain()
    {
		readProperties();
    }
	
    /**
     * Creates main class of user_client when run in GUI mode
     * @param guiMain
     */
    public ClientMain(GuiMain guiMain)
    {
        this();
        _gui = guiMain;
        _gui.setClientMain(this); // set object for callbacks
    }

	/**
	 * Main function
	 * @param args command line arguments
	 */
	public static void main(String[] args)
    {
        logger.dbo(DebugLevel.L5_DEBUG, "Supported audio file types", AudioSystem.getAudioFileTypes());
        logger.debugObject("Graphic environment supported: ",
                           new Boolean(!GraphicsEnvironment.isHeadless()));
        if (GraphicsEnvironment.isHeadless())
        {
            // non gui mode
            new ClientMain();
        }
        else
        {
             // gui mode
             GuiMain guiMain = new GuiMain();
             new ClientMain(guiMain);
        }
    }
	
    private void readProperties()
    {
    	BufferedReader reader;
    	try
    	{
    		reader = new BufferedReader(new FileReader(configFileName));
    		logger.debugObject("Reading properties from: ", configFileName);
    		String line;
    		while((line = reader.readLine()) != null)
    		{
    			if(line.isEmpty() || line.trim().startsWith("#") || line.trim().startsWith("//"))
    			{
    				logger.ludicrousObject("Skipping comment line:", line);
    				continue;
    			}
    			// split the line into tokens with '=' character as delimiter
    			String[] tokens = line.split("=");
    			
    			logger.ludicrousObject("Property line split into tokens", tokens);
    			
    			if(tokens.length == 1)
    			{
    				logger.verboseObject("Property line didn't have '=' character - skipping. Line:", line);
    				continue;
    			}
    			
    			String paramName = tokens[0].trim();
    			
    			// check if the first token (key) is valid
    			if(paramName.equalsIgnoreCase("sgw_ip"))
    			{
    				logger.debugObject("Property sgw_ip found, value=", tokens[1].trim());
    				InetAddress sgwAddress = InetAddress.getByName(tokens[1].trim());
    				
    				logger.debugObject("Property sgw_ip created InetAddress:", sgwAddress);
    				
    				// TODO set constant for port number
    				// currently set to port value from SgwStub
    				UapSgwProxy.init(sgwAddress, 9696);
    				continue;
    			}
    			else if(paramName.equalsIgnoreCase("user_client_id"))
    			{
    			    String valueString = tokens[1].trim();
    			    Integer clientId = null;
    			    try
    			    {
        			    if(valueString.startsWith("0x") || valueString.startsWith("0X"))
    			        {
        			        clientId = Integer.parseInt(valueString.substring(2), 16);
    			        }
        			    else
        			    {
        			        clientId = Integer.parseInt(valueString);
        			    }
    			    } catch (NumberFormatException e)
    			    {
    			        logger.error("Unable to parse value of user_client_id parameter, value="+valueString);
    			        logger.errorException(e);
    			        continue;
    			    }
    			    
    			    _clientId = clientId.intValue();
    			    logger.debugObject("User client ID set to:0x", Integer.toHexString(_clientId));
    			    if(_clientId > 0x00FFFFFF)
    			    {
    			        logger.error("Value of user_client_id is invalid, allowed values: [1 ... 2^24-1]");
    			        System.exit(-1);
    			    }
    			    continue;
    			}
    			else
    			{
    			    logger.warn("Property line not matched: "+line);
    			}
    		}
    		reader.close();
    	} catch (FileNotFoundException e)
    	{
    		logger.errorException(e);
    	} catch (IOException e)
    	{
    		logger.errorException(e);
    	}
    }

    /**
     * Orders client to establish signaling connection.
     * It first creates new UapGenerator (state machine) which then handles
     * UAP protocol signaling
     * 
     * @return <code>true</code> if sending of UapConnectionReq was successful
     * and connection establishment is in progress, <code>false</code> otherwise
     */
    public boolean establishConnection()
    {
        logger.debug("Trying to establish UAP connection");
        if(_uapGenerator == null)
        {
            // if gui is set then use it as a listener otherwise use this class
            _uapGenerator = new UapGenerator(_gui != null ? _gui : this, _clientId);
        }
        return UapSgwProxy.instance().sendUapConnectionReq(_uapGenerator);
    }

    /* (non-Javadoc)
     * @see three_star_craft.uap.UapConnectionListener#connectionEstablishSuccessInd(three_star_craft.uap.UapGenerator, java.util.List)
     */
    public void connectionEstablishSuccessInd(UapGenerator gen,
            List<PlayableItem> playableItems)
    {
        setAvailableMedia(playableItems);
    }

    /* (non-Javadoc)
     * @see three_star_craft.uap.UapConnectionListener#connectionEstablishFailInd(three_star_craft.uap.UapGenerator, UserApplicationPart.UAP.Cause)
     */
    public void connectionEstablishFailInd(UapGenerator gen, UAP.Cause cause)
    {
        
    }

    /* (non-Javadoc)
     * @see three_star_craft.uap.UapConnectionListener#connectionEstablishTimeout(three_star_craft.uap.UapGenerator)
     */
    public void connectionEstablishTimeout(UapGenerator gen)
    {
        
    }

    /* (non-Javadoc)
     * @see three_star_craft.uap.UapConnectionListener#mediaBearerSetupTimeout(three_star_craft.uap.UapGenerator)
     */
    public void mediaBearerSetupTimeout(UapGenerator uapGenerator)
    {
        
    }
    
    
    /* (non-Javadoc)
     * @see three_star_craft.uap.UapConnectionListener#mediaBearerSetupConfirmInd(three_star_craft.uap.UapGenerator)
     */
    public void mediaBearerSetupConfirmInd(UapGenerator generator)
    {
        // TODO Auto-generated method stub
        
    }

    /**
     * Stores list of media items available for this generator 
     * @param playableItems
     */
    public void setAvailableMedia(List<PlayableItem> playableItems)
    {
        if(_availableMedia == null)
        {
            _availableMedia = new ArrayList<PlayableItem>();
        }
        else
        {
            _availableMedia.clear();
        }
        _availableMedia.addAll(playableItems);
    }

    /**
     * Client if ordered to establish media bearer for transmission
     * of media item with specified ID
     * @param mediaItemId an ID of media item to play
     * @return <code>true</code> if UapMediaBearerSetupReq was send,
     * <code>false</code> otherwise
     */
    public boolean establishBearer(int mediaItemId)
    {
        if(_uapGenerator == null)
        {
            logger.error("Trying to setup MediaBearer but generator is null");
            return false;
        }
        // initiate media socket in the UapGenerator
        if(!_uapGenerator.initiateMediaSocket())
        {
            logger.warn("Failed to initiate socket for media transmission");
            return false;
        }
        
        if(UapSgwProxy.instance().sendMediaBearerRequest(_uapGenerator, mediaItemId))
        {
            _uapGenerator.changeState(UapStateMap.UAP_MEDIA_BEARER_SETUP_PEND);
            _uapGenerator.startTWaitForMediaBearerCfm();
            return true;
        }
        
        logger.debugObject("Failed to send UapMediaBearerReq for generator:",
                _uapGenerator.getDebugInfo());
        // we've failed to setup connection so we need to clean up
        _uapGenerator.closeMediaSocket();
        
        return false;
    }
    
    /**
     * Orders client to release all resources, i.e media bearers
     * and signaling connection
     */
    public void releaseAll()
    {
        if(_uapGenerator != null && _uapGenerator.isConnected())
        {
            UapSgwProxy.instance().sendUapConnectionRelease(_uapGenerator);
            _uapGenerator.changeState(UapStateMap.UAP_DISCONNECT_PEND);
        }
        if(_availableMedia != null)
        {
            _availableMedia.clear();
        }
    }
    
    public void connectionReleaseInd(UapGenerator gen)
    {
        
    }
}
